﻿namespace System.ServiceModel.DomainServices.Client
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.ServiceModel.DomainServices;
    using System.Threading;

    internal class ChangeSetBuilder
    {
        public static List<ChangeSetEntry> Build(EntityChangeSet changeSet)
        {
            CheckForInvalidUpdates(changeSet);
            List<ChangeSetEntry> changeSetEntries = BuildOperations(changeSet);
            UnmodifiedOperationAdder.Add(changeSetEntries);
            AssociationMapBuilder.Build(changeSetEntries);
            return changeSetEntries;
        }

        private static List<ChangeSetEntry> BuildOperations(EntityChangeSet changeSet)
        {
            List<ChangeSetEntry> list = new List<ChangeSetEntry>();
            int num = 0;
            EntityOperationType none = EntityOperationType.None;
            foreach (Entity entity in (IEnumerable<Entity>) changeSet)
            {
                switch (entity.EntityState)
                {
                    case EntityState.Modified:
                        none = EntityOperationType.Update;
                        break;

                    case EntityState.New:
                        none = EntityOperationType.Insert;
                        break;

                    case EntityState.Deleted:
                        none = EntityOperationType.Delete;
                        break;

                    default:
                    {
                        continue;
                    }
                }
                ChangeSetEntry item = new ChangeSetEntry(entity, num++, none);
                if (entity.OriginalValues != null)
                {
                    if (entity.MetaType.ShouldRoundtripOriginal && (entity.OriginalValues != null))
                    {
                        item.OriginalEntity = GetRoundtripEntity(entity);
                    }
                    else
                    {
                        item.HasMemberChanges = true;
                    }
                }
                if (entity.CustomMethodInvocation != null)
                {
                    if (string.IsNullOrEmpty(entity.CustomMethodInvocation.Name))
                    {
                        throw new ArgumentException(Resource.DomainClient_InvocationNameCannotBeNullOrEmpty);
                    }
                    if (item.EntityActions == null)
                    {
                        item.EntityActions = new Dictionary<string, object[]>();
                    }
                    item.EntityActions.Add(entity.CustomMethodInvocation.Name, entity.CustomMethodInvocation.Parameters.ToArray<object>());
                }
                list.Add(item);
            }
            return list;
        }

        internal static void CheckForInvalidUpdates(EntityChangeSet changeSet)
        {
            AssociationUpdateChecker checker = new AssociationUpdateChecker();
            foreach (Entity entity in (IEnumerable<Entity>) changeSet)
            {
                if (entity.EntityState == EntityState.Modified)
                {
                    foreach (PropertyInfo info in entity.ModifiedProperties)
                    {
                        if (info.GetCustomAttributes(typeof(KeyAttribute), false).Any<object>())
                        {
                            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Entity_KeyMembersCannotBeChanged, new object[] { info.Name, entity.GetType().Name }));
                        }
                    }
                }
                checker.Visit(entity);
            }
        }

        internal static Entity GetRoundtripEntity(Entity entity)
        {
            Entity entity2 = (Entity) Activator.CreateInstance(entity.GetType());
            IDictionary<string, object> entityStateToApply = ObjectStateUtility.ExtractRoundtripState(entity.GetType(), entity.OriginalValues);
            entity2.ApplyState(entityStateToApply);
            return entity2;
        }

        internal class AssociationMapBuilder : EntityVisitor
        {
            private List<ChangeSetEntry> _changeSetEntries;
            private ChangeSetEntry _currentChangeSetEntry;
            private Dictionary<Entity, int> _entityIdMap;

            private AssociationMapBuilder(List<ChangeSetEntry> changeSetEntries)
            {
                this._entityIdMap = Enumerable.ToDictionary<ChangeSetEntry, Entity, int>(changeSetEntries, (Func<ChangeSetEntry, Entity>) (p => p.Entity), (Func<ChangeSetEntry, int>) (p => p.Id));
                this._changeSetEntries = changeSetEntries;
            }

            private void Build()
            {
                foreach (IGrouping<Type, ChangeSetEntry> grouping in from p in this._changeSetEntries group p by p.Entity.GetType())
                {
                    foreach (ChangeSetEntry entry in grouping)
                    {
                        this._currentChangeSetEntry = entry;
                        this.Visit(entry.Entity);
                    }
                }
            }

            public static void Build(List<ChangeSetEntry> changeSetEntries)
            {
                new ChangeSetBuilder.AssociationMapBuilder(changeSetEntries).Build();
            }

            private IEnumerable<ChangeSetEntry> FindOriginalChildren(AssociationAttribute association)
            {
                foreach (ChangeSetEntry iteratorVariable0 in from p in this._changeSetEntries
                    where p.Entity.EntityState == EntityState.Deleted
                    select p)
                {
                    Entity parent = iteratorVariable0.Entity.Parent;
                    if (((parent != null) && (parent == this._currentChangeSetEntry.Entity)) && (iteratorVariable0.Entity.ParentAssociation.Name == association.Name))
                    {
                        yield return iteratorVariable0;
                    }
                }
            }

            protected override void VisitEntityCollection(IEntityCollection entityCollection, PropertyInfo propertyInfo)
            {
                if (!propertyInfo.GetCustomAttributes(typeof(ExternalReferenceAttribute), true).Any<object>())
                {
                    bool flag = propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Any<object>();
                    List<int> list = new List<int>();
                    List<int> list2 = new List<int>();
                    if (entityCollection.HasValues)
                    {
                        foreach (Entity entity in entityCollection.Entities)
                        {
                            int num;
                            if (!this._entityIdMap.TryGetValue(entity, out num))
                            {
                                continue;
                            }
                            bool flag2 = (this._currentChangeSetEntry.Entity.EntityState != EntityState.Deleted) && (entity.EntityState == EntityState.New);
                            if (flag || flag2)
                            {
                                list.Add(num);
                            }
                            if (flag && (entity.EntityState != EntityState.New))
                            {
                                list2.Add(num);
                            }
                        }
                    }
                    if (flag)
                    {
                        int[] source = (from p in this.FindOriginalChildren(entityCollection.Association) select p.Id).ToArray<int>();
                        if (source.Length > 0)
                        {
                            list2.AddRange(source.ToArray<int>());
                        }
                    }
                    if (list.Count > 0)
                    {
                        Dictionary<string, int[]> associations = (Dictionary<string, int[]>) this._currentChangeSetEntry.Associations;
                        if (associations == null)
                        {
                            associations = new Dictionary<string, int[]>();
                            this._currentChangeSetEntry.Associations = associations;
                        }
                        associations.Add(propertyInfo.Name, list.ToArray());
                    }
                    if (list2.Count > 0)
                    {
                        Dictionary<string, int[]> originalAssociations = (Dictionary<string, int[]>) this._currentChangeSetEntry.OriginalAssociations;
                        if (originalAssociations == null)
                        {
                            originalAssociations = new Dictionary<string, int[]>();
                            this._currentChangeSetEntry.OriginalAssociations = originalAssociations;
                        }
                        originalAssociations.Add(propertyInfo.Name, list2.ToArray());
                    }
                }
            }

            protected override void VisitEntityRef(IEntityRef entityRef, Entity parent, PropertyInfo propertyInfo)
            {
                if (!propertyInfo.GetCustomAttributes(typeof(ExternalReferenceAttribute), true).Any<object>())
                {
                    Entity key = null;
                    if ((entityRef != null) && entityRef.HasValue)
                    {
                        key = entityRef.Entity;
                    }
                    Entity entity = null;
                    bool flag = propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Any<object>();
                    if (flag && (parent.EntityState != EntityState.New))
                    {
                        AssociationAttribute association = (AssociationAttribute) propertyInfo.GetCustomAttributes(typeof(AssociationAttribute), false).Single<object>();
                        ChangeSetEntry entry = this.FindOriginalChildren(association).SingleOrDefault<ChangeSetEntry>();
                        if (entry != null)
                        {
                            entity = entry.Entity;
                        }
                    }
                    if (flag && (entity == null))
                    {
                        entity = key;
                    }
                    int num = -1;
                    bool flag2 = (key != null) && ((this._currentChangeSetEntry.Entity.EntityState != EntityState.Deleted) && (key.EntityState == EntityState.New));
                    if (((key != null) && (flag2 || flag)) && this._entityIdMap.TryGetValue(key, out num))
                    {
                        Dictionary<string, int[]> associations = (Dictionary<string, int[]>) this._currentChangeSetEntry.Associations;
                        if (associations == null)
                        {
                            associations = new Dictionary<string, int[]>();
                            this._currentChangeSetEntry.Associations = associations;
                        }
                        associations.Add(propertyInfo.Name, new int[] { num });
                    }
                    if (((entity != null) && flag) && this._entityIdMap.TryGetValue(entity, out num))
                    {
                        Dictionary<string, int[]> originalAssociations = (Dictionary<string, int[]>) this._currentChangeSetEntry.OriginalAssociations;
                        if (originalAssociations == null)
                        {
                            originalAssociations = new Dictionary<string, int[]>();
                            this._currentChangeSetEntry.OriginalAssociations = originalAssociations;
                        }
                        originalAssociations.Add(propertyInfo.Name, new int[] { num });
                    }
                }
            }

        }

        internal class AssociationUpdateChecker : EntityVisitor
        {
            private static void CheckInvalidChildUpdates(Entity entity, AssociationAttribute compositionAttribute)
            {
                if (((compositionAttribute != null) && (entity.EntityState == EntityState.Modified)) && (from p in entity.ModifiedProperties select p.Name).Intersect<string>(compositionAttribute.OtherKeyMembers).Any<string>())
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.Entity_CantReparentComposedChild, new object[] { entity }));
                }
            }

            protected override void VisitEntityCollection(IEntityCollection entityCollection, PropertyInfo propertyInfo)
            {
                if ((propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Length == 1) && entityCollection.HasValues)
                {
                    AssociationAttribute compositionAttribute = (AssociationAttribute) propertyInfo.GetCustomAttributes(typeof(AssociationAttribute), false).SingleOrDefault<object>();
                    foreach (Entity entity in entityCollection.Entities)
                    {
                        CheckInvalidChildUpdates(entity, compositionAttribute);
                    }
                }
            }

            protected override void VisitEntityRef(IEntityRef entityRef, Entity parent, PropertyInfo propertyInfo)
            {
                Entity entity = null;
                if ((entityRef != null) && entityRef.HasValue)
                {
                    entity = entityRef.Entity;
                }
                if ((entity != null) && (propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Length == 1))
                {
                    AssociationAttribute compositionAttribute = (AssociationAttribute) propertyInfo.GetCustomAttributes(typeof(AssociationAttribute), false).SingleOrDefault<object>();
                    CheckInvalidChildUpdates(entity, compositionAttribute);
                }
            }
        }

        internal class UnmodifiedOperationAdder : EntityVisitor
        {
            private List<ChangeSetEntry> _changeSetEntries;
            private int _id;
            private bool _isChild;
            private Dictionary<object, bool> _visited = new Dictionary<object, bool>();

            public UnmodifiedOperationAdder(List<ChangeSetEntry> changeSetEntries)
            {
                this._changeSetEntries = changeSetEntries;
                if (this._changeSetEntries.Any<ChangeSetEntry>())
                {
                    this._id = Enumerable.Max<ChangeSetEntry>(this._changeSetEntries, (Func<ChangeSetEntry, int>) (p => p.Id)) + 1;
                }
            }

            public static void Add(List<ChangeSetEntry> changeSetEntries)
            {
                Entity[] entityArray = (from p in changeSetEntries
                    where p.Entity.MetaType.HasComposition
                    select p.Entity).ToArray<Entity>();
                if (entityArray.Length != 0)
                {
                    ChangeSetBuilder.UnmodifiedOperationAdder adder = new ChangeSetBuilder.UnmodifiedOperationAdder(changeSetEntries);
                    foreach (Entity entity in entityArray)
                    {
                        adder.Visit(entity);
                    }
                }
            }

            public override void Visit(Entity entity)
            {
                if (!this._visited.ContainsKey(entity))
                {
                    if (this._isChild && (entity.EntityState == EntityState.Unmodified))
                    {
                        ChangeSetEntry item = new ChangeSetEntry(entity, this._id++, EntityOperationType.None);
                        this._changeSetEntries.Add(item);
                    }
                    this._visited.Add(entity, true);
                    base.Visit(entity);
                }
            }

            protected override void VisitEntityCollection(IEntityCollection entityCollection, PropertyInfo propertyInfo)
            {
                if (propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Any<object>())
                {
                    bool flag = this._isChild;
                    this._isChild = true;
                    foreach (Entity entity in entityCollection.Entities)
                    {
                        this.Visit(entity);
                    }
                    this._isChild = flag;
                }
            }

            protected override void VisitEntityRef(IEntityRef entityRef, Entity parent, PropertyInfo propertyInfo)
            {
                if (propertyInfo.GetCustomAttributes(typeof(CompositionAttribute), false).Any<object>())
                {
                    Entity entity = null;
                    if (entityRef == null)
                    {
                        entity = (Entity) propertyInfo.GetValue(parent, null);
                    }
                    else
                    {
                        entity = entityRef.Entity;
                    }
                    if (entity != null)
                    {
                        bool flag = this._isChild;
                        this._isChild = true;
                        this.Visit(entity);
                        this._isChild = flag;
                    }
                }
            }
        }
    }
}

